#include "SQLIndexDefinition.h"
#include "../visitor/SQLASTVisitor.h"
#include "../../utils/FnvHash.h"
#include "../../utils/instanceof.h"
#include "expr/SQLIdentifierExpr.h"

SQLIndexDefinition::SQLIndexDefinition()
{
  compatibleOptions = SQLAssignItem_list_ptr();
  covering = SQLName_list_ptr();
  columns = SQLSelectOrderByItem_list_ptr();
}

BOOL_ptr SQLIndexDefinition::getHasConstraint()
{
  return hasConstraint;
}

void SQLIndexDefinition::setHasConstraint(BOOL_ptr hasConstraint)
{
  this->hasConstraint = hasConstraint;
}

SQLName_ptr SQLIndexDefinition::getSymbol()
{
  return symbol;
}

void SQLIndexDefinition::setSymbol(SQLName_ptr symbol)
{
  if (symbol != NULL)
  {
    if (getParent() != NULL)
    {
      symbol->setParent(getParent());
    }
    else
    {
      symbol->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->symbol = symbol;
}

BOOL_ptr SQLIndexDefinition::isGlobal()
{
  return global;
}

void SQLIndexDefinition::setGlobal(BOOL_ptr global)
{
  this->global = global;
}

BOOL_ptr SQLIndexDefinition::isLocal()
{
  return local;
}

void SQLIndexDefinition::setLocal(BOOL_ptr local)
{
  this->local = local;
}

string_ptr SQLIndexDefinition::getType()
{
  return type;
}

void SQLIndexDefinition::setType(string_ptr type)
{
  this->type = type;
}

BOOL_ptr SQLIndexDefinition::isHashMapType()
{
  return hashMapType;
}

void SQLIndexDefinition::setHashMapType(BOOL_ptr hashMapType)
{
  this->hashMapType = hashMapType;
}

BOOL_ptr SQLIndexDefinition::isHashType()
{
  return hashType;
}

void SQLIndexDefinition::setHashType(BOOL_ptr hashType)
{
  this->hashType = hashType;
}

BOOL_ptr SQLIndexDefinition::isIndex()
{
  return index;
}

void SQLIndexDefinition::setIndex(BOOL_ptr index)
{
  this->index = index;
}

BOOL_ptr SQLIndexDefinition::isKey()
{
  return key;
}

void SQLIndexDefinition::setKey(BOOL_ptr key)
{
  this->key = key;
}

SQLName_ptr SQLIndexDefinition::getName()
{
  return name;
}

void SQLIndexDefinition::setName(SQLName_ptr name)
{
  if (name != NULL)
  {
    if (getParent() != NULL)
    {
      name->setParent(getParent());
    }
    else
    {
      name->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->name = name;
}

SQLTableSource_ptr SQLIndexDefinition::getTable()
{
  return table;
}

void SQLIndexDefinition::setTable(SQLTableSource_ptr table)
{
  if (table != NULL)
  {
    if (getParent() != NULL)
    {
      table->setParent(getParent());
    }
    else
    {
      table->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->table = table;
}

SQLSelectOrderByItem_list_ptr SQLIndexDefinition::getColumns()
{
  return columns;
}

void SQLIndexDefinition::setColumns(SQLSelectOrderByItem_list_ptr columns)
{
  this->columns = columns;
}

BOOL_ptr SQLIndexDefinition::hasOptions()
{
  return (options != NULL) ? BOOL::TRUE : BOOL::FALSE;
}

SQLIndexOptions_ptr SQLIndexDefinition::getOptions()
{
  if (NULL == options)
  {
    options = SQLIndexOptions_ptr(new SQLIndexOptions());
    options->setParent(SharedObject(SQLIndexDefinition));
  }
  return options;
}

SQLExpr_ptr SQLIndexDefinition::getDbPartitionBy()
{
  return dbPartitionBy;
}

void SQLIndexDefinition::setDbPartitionBy(SQLExpr_ptr dbPartitionBy)
{
  if (dbPartitionBy != NULL)
  {
    if (getParent() != NULL)
    {
      dbPartitionBy->setParent(getParent());
    }
    else
    {
      dbPartitionBy->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->dbPartitionBy = dbPartitionBy;
}

SQLExpr_ptr SQLIndexDefinition::getTbPartitionBy()
{
  return tbPartitionBy;
}

void SQLIndexDefinition::setTbPartitionBy(SQLExpr_ptr tbPartitionBy)
{
  if (tbPartitionBy != NULL)
  {
    if (getParent() != NULL)
    {
      tbPartitionBy->setParent(getParent());
    }
    else
    {
      tbPartitionBy->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->tbPartitionBy = tbPartitionBy;
}

SQLExpr_ptr SQLIndexDefinition::getTbPartitions()
{
  return tbPartitions;
}

void SQLIndexDefinition::setTbPartitions(SQLExpr_ptr tbPartitions)
{
  if (tbPartitions != NULL)
  {
    if (getParent() != NULL)
    {
      tbPartitions->setParent(getParent());
    }
    else
    {
      tbPartitions->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->tbPartitions = tbPartitions;
}

SQLName_list_ptr SQLIndexDefinition::getCovering()
{
  return covering;
}

void SQLIndexDefinition::setCovering(SQLName_list_ptr covering)
{
  this->covering = covering;
}

SQLName_ptr SQLIndexDefinition::getAnalyzerName()
{
  return analyzerName;
}

void SQLIndexDefinition::setAnalyzerName(SQLName_ptr analyzerName)
{
  if (analyzerName != NULL)
  {
    if (getParent() != NULL)
    {
      analyzerName->setParent(getParent());
    }
    else
    {
      analyzerName->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->analyzerName = analyzerName;
}

SQLName_ptr SQLIndexDefinition::getIndexAnalyzerName()
{
  return indexAnalyzerName;
}

void SQLIndexDefinition::setIndexAnalyzerName(SQLName_ptr indexAnalyzerName)
{
  if (indexAnalyzerName != NULL)
  {
    if (getParent() != NULL)
    {
      indexAnalyzerName->setParent(getParent());
    }
    else
    {
      indexAnalyzerName->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->indexAnalyzerName = indexAnalyzerName;
}

SQLName_ptr SQLIndexDefinition::getQueryAnalyzerName()
{
  return queryAnalyzerName;
}

void SQLIndexDefinition::setQueryAnalyzerName(SQLName_ptr queryAnalyzerName)
{
  if (queryAnalyzerName != NULL)
  {
    if (getParent() != NULL)
    {
      queryAnalyzerName->setParent(getParent());
    }
    else
    {
      queryAnalyzerName->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->queryAnalyzerName = queryAnalyzerName;
}

SQLName_ptr SQLIndexDefinition::getWithDicName()
{
  return withDicName;
}

void SQLIndexDefinition::setWithDicName(SQLName_ptr withDicName)
{
  if (withDicName != NULL)
  {
    if (getParent() != NULL)
    {
      withDicName->setParent(getParent());
    }
    else
    {
      withDicName->setParent(SharedObject(SQLIndexDefinition));
    }
  }
  this->withDicName = withDicName;
}

SQLAssignItem_list_ptr SQLIndexDefinition::getCompatibleOptions()
{
  return compatibleOptions;
}

void SQLIndexDefinition::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLIndexDefinition)))
  {
    if (name != NULL)
    {
      name->accept(visitor);
    }

    for (SQLSelectOrderByItem_ptr item : *columns)
    {
      if (item != NULL)
      {
        item->accept(visitor);
      }
    }
    for (SQLName_ptr item : *covering)
    {
      if (item != NULL)
      {
        item->accept(visitor);
      }
    }
  }
  visitor->endVisit(SharedObject(SQLIndexDefinition));
}

void SQLIndexDefinition::cloneTo(SQLIndexDefinition_ptr definition)
{
  SQLObject_ptr parent;
  if (definition->getParent() != NULL)
  {
    parent = definition->getParent();
  }
  else
  {
    parent = std::dynamic_pointer_cast<SQLObject>(definition);
  }
  definition->hasConstraint = hasConstraint;
  if (symbol != NULL)
  {
    definition->symbol = std::dynamic_pointer_cast<SQLName>(symbol->clone());
    definition->symbol->setParent(parent);
  }
  definition->global = global;
  definition->local = local;
  definition->type = type;
  definition->hashMapType = hashMapType;
  definition->index = index;
  definition->key = key;
  if (name != NULL)
  {
    definition->name = std::dynamic_pointer_cast<SQLName>(name->clone());
    definition->name->setParent(parent);
  }
  if (table != NULL)
  {
    definition->table = std::dynamic_pointer_cast<SQLTableSource>(table->clone());
    definition->table->setParent(parent);
  }
  for (SQLSelectOrderByItem_ptr item : *columns)
  {
    SQLSelectOrderByItem_ptr item1 = std::dynamic_pointer_cast<SQLSelectOrderByItem>(item->clone());
    item1->setParent(parent);
    definition->columns->push_back(item1);
  }
  if (options != NULL)
  {
    options->cloneTo(definition->getOptions());
  }
  if (dbPartitionBy != NULL)
  {
    definition->dbPartitionBy = std::dynamic_pointer_cast<SQLExpr>(dbPartitionBy->clone());
    definition->dbPartitionBy->setParent(parent);
  }
  if (tbPartitionBy != NULL)
  {
    definition->tbPartitionBy = std::dynamic_pointer_cast<SQLExpr>(tbPartitionBy->clone());
    definition->tbPartitionBy->setParent(parent);
  }
  if (tbPartitions != NULL)
  {
    definition->tbPartitions = std::dynamic_pointer_cast<SQLExpr>(tbPartitions->clone());
    definition->tbPartitions->setParent(parent);
  }
  for (SQLName_ptr name : *covering)
  {
    SQLName_ptr name1 = std::dynamic_pointer_cast<SQLName>(name->clone());
    name1->setParent(parent);
    definition->covering->push_back(name1);
  }
  if (analyzerName != NULL)
  {
    definition->analyzerName = std::dynamic_pointer_cast<SQLName>(analyzerName->clone());
    definition->analyzerName->setParent(parent);
  }
  if (indexAnalyzerName != NULL)
  {
    definition->indexAnalyzerName = std::dynamic_pointer_cast<SQLName>(indexAnalyzerName->clone());
    definition->indexAnalyzerName->setParent(parent);
  }
  if (withDicName != NULL)
  {
    definition->withDicName = std::dynamic_pointer_cast<SQLName>(withDicName->clone());
    definition->withDicName->setParent(parent);
  }
  if (queryAnalyzerName != NULL)
  {
    definition->queryAnalyzerName = std::dynamic_pointer_cast<SQLName>(queryAnalyzerName->clone());
    definition->queryAnalyzerName->setParent(parent);
  }
  for (SQLAssignItem_ptr item : *compatibleOptions)
  {
    SQLAssignItem_ptr item2 = std::dynamic_pointer_cast<SQLAssignItem>(item->clone());
    item2->setParent(parent);
    definition->compatibleOptions->push_back(item2);
  }
}

//
// Function for compatibility->
//

void SQLIndexDefinition::addOption(string_ptr name, SQLExpr_ptr value)
{
  SQLAssignItem_ptr assignItem =
      SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(name)), value));
  if (getParent() != NULL)
  {
    assignItem->setParent(getParent());
  }
  else
  {
    assignItem->setParent(SharedObject(SQLIndexDefinition));
  }
  getCompatibleOptions()->push_back(assignItem);
}

SQLExpr_ptr SQLIndexDefinition::getOption(string_ptr name)
{
  if (name->empty())
  {
    return NULL;
  }

  return getOption(FnvHash::hashCode64(name));
}

SQLExpr_ptr SQLIndexDefinition::getOption(long hash64)
{
  // Search in compatible list first->
  for (SQLAssignItem_ptr item : *compatibleOptions)
  {
    SQLExpr_ptr target = item->getTarget();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(target))
    {
      if ((std::dynamic_pointer_cast<SQLIdentifierExpr>(target))->hashCode64() == hash64)
      {
        return item->getValue();
      }
    }
  }

  // Now search in new options->
  if (NULL == options)
  {
    return NULL;
  }

  if (hash64 == FnvHash::Constants::KEY_BLOCK_SIZE)
  {
    return options->getKeyBlockSize();
  }
  else if (hash64 == FnvHash::Constants::ALGORITHM)
  {
    if (!options->getAlgorithm()->empty())
    {
      return SQLIdentifierExpr_ptr(new SQLIdentifierExpr(options->getAlgorithm()));
    }
    return NULL;
  }
  else if (hash64 == FnvHash::hashCode64(make_string_ptr("LOCK")))
  {
    if (!options->getLock()->empty())
    {
      return SQLIdentifierExpr_ptr(new SQLIdentifierExpr(options->getLock()));
    }
    return NULL;
  }

  for (SQLAssignItem_ptr item : *options->getOtherOptions())
  {
    SQLExpr_ptr target = item->getTarget();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(target))
    {
      if ((std::dynamic_pointer_cast<SQLIdentifierExpr>(target))->hashCode64() == hash64)
      {
        return item->getValue();
      }
    }
  }

  return NULL;
}

string_ptr SQLIndexDefinition::getDistanceMeasure()
{
  SQLExpr_ptr expr = getOption(FnvHash::Constants::DISTANCEMEASURE);
  if (expr == NULL)
  {
    return NULL;
  }

  return expr->toString();
}

string_ptr SQLIndexDefinition::getAlgorithm()
{
  if (options != NULL && !options->getAlgorithm()->empty())
  {
    return options->getAlgorithm();
  }

  SQLExpr_ptr expr = getOption(FnvHash::Constants::ALGORITHM);
  if (expr == NULL)
  {
    return NULL;
  }

  return expr->toString();
}
